// Includes
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <limits.h>
#include <iostream>
#include <string>
#include <ctime>
#include <cstdlib>

//Incluyo la clase Mersenne-Twister para la generación de números aleatorios en C++
#include "mt.h"

//Incluyo la clase Random para la generación de números aleatorios en CUDA
#include "Random.cu"

//Incluyo la clase cuPrintf para poder imprimir desde GPU
#include "cuPrintf.cu"

//Incluyo la clase parser.cpp para obetener los parametros
#include "parser.cpp"

//Semilla aleatoria a utilizar.
#define SEMILLA (time(NULL))

using namespace std;

/******************* Declaracion de Variables globales del Host ****************/
int tipoRepresentacion;
int totalGenes;
int totalIndividuos;
int cantidadPoblaciones;// = 1;
int cantCromosomasPoblacion;// = 2;
int cantGenesEnCromosoma;// = 8;
int numeroGeneraciones;

/******************* Declaracion de Variables globales del Device ****************/
__device__ int cantidadPoblacionesDevice;
__device__ int cantCromosomasPoblacionDevice;
__device__ int cantGenesEnCromosomaDevice;
__device__ int totalGenesEnPoblacionDevice;
__device__ int tipoCruzamiento;
__device__ int tipoMutacion;
__device__ int probabilidadCruzamiento;
__device__ int probabilidadMutacion;
__device__ int tipoReemplazo;
__device__ int tipoSeleccion;
__device__ int numeroGeneracionesDevice;
__device__ int gradoNoUniformidad;

/******************* Declaracion de Funciones del Host *************************/
void representacionBinaria();
void representacionEntera();
void representacionReal();
void asignarParametros(int * parametros);

/******************* Declaracion de Funciones Global del Device ********************/
__global__ void asignarParametrosDevice(int * parametros);
__global__ void inicializarPoblacion(int* poblacion, int* fitnessActualDevice, struct Random randomGPU, int cantCromosomasPoblacion, int ejecutarFuncion);
__global__ void inicializarPoblacionReal(float* poblacion, float* fitnessActualDevice, struct Random randomGPU, int cantCromosomasPoblacion, int ejecutarFuncion);
__global__ void mainKernelBinaria(int* poblacionActualDevice, int* nuevaPoblacionDevice, int* fitnessActualDevice, int* nuevoFitnessDevice, int* individuosAMigrarDevice, struct Random r);
__global__ void mainKernelEntera(int* poblacionActualDevice, int* nuevaPoblacionDevice, int* fitnessActualDevice, int* nuevoFitnessDevice, int* individuosAMigrarDevice, struct Random r);
__global__ void mainKernelReal(float* poblacionActualDevice, float* nuevaPoblacionDevice, float* fitnessActualDevice, float* nuevoFitnessDevice, float* individuosAMigrarDevice, struct Random r, int generacionActual);
__global__ void migracionRepresentacionBinaria(int* poblacionActualDevice, int* fitnessActualDevice, int* individuosAMigrarDevice, struct Random randomGPU);

/******************* Declaracion de Funciones internas del Device *****************/
__device__ void inicializarPoblacionBinaria(int* poblacion, int* fitnessActualDevice, struct Random randomGPU, int cantCromosomasPoblacion);
__device__ void inicializarPoblacionEntera(int* poblacion, int* fitnessActualDevice, struct Random randomGPU, int cantCromosomasPoblacion);
__device__ void inicializarPoblacionPermutacionEntera(int* poblacion, int* fitnessActualDevice, struct Random randomGPU, int cantCromosomasPoblacion);
__device__ void individosAMigrarRepresentacionBinaria(int* nuevoFitnessDevice, int* nuevaPoblacionDevice, int* individuosAMigrarDevice);
__device__ void asignarFitness(int* poblacion, int* vectorFitness);
__device__ int seleccionBinaria(int* vectorFitness, struct Random randomGPU, int ejecutarFuncion, int indiceMejor);
__device__ int seleccionEntera(int* vectorFitness, struct Random randomGPU, int ejecutarFuncion, int indiceMejor);
__device__ int seleccionReal(float* vectorFitness, struct Random randomGPU, int ejecutarFuncion, int indiceMejor);
__device__ int seleccionPorTorneoBinaria(int* vectorFitness, struct Random randomGPU);
__device__ int seleccionPorTorneoEntera(int* vectorFitness, struct Random randomGPU);
__device__ int seleccionPorTorneoReal(float* vectorFitness, struct Random randomGPU);
__device__ int seleccionElitistaBinaria(int* vectorFitness, int indiceMejor);
__device__ int seleccionElitistaEntera(int* vectorFitness, int indiceMejor);
__device__ int seleccionElitistaReal(float* vectorFitness, int indiceMejor);
__device__ void mutacionBinaria(int* poblacion, int probabilidad, struct Random randomGPU, int ejecutarFuncion);
__device__ void mutacionEntera(int* poblacion, int probabilidad, struct Random randomGPU, int ejecutarFuncion);
__device__ void mutacionReal(float* poblacion, int probabilidad, struct Random randomGPU, int ejecutarFuncion, int generacionActual);
__device__ void mutacionPorInversion(int* poblacion, int probabilidad, struct Random randomGPU);
__device__ void mutacionReajusteAleatorio(int* poblacion, int probabilidad, struct Random randomGPU);
__device__ void mutacionPorDeslizamiento(int* poblacion, int probabilidad, struct Random randomGPU);
__device__ void mutacionPorIntercambio(int* poblacion, int probabilidad, struct Random randomGPU);
__device__ void mutacionPorInsercionSimple(int* poblacion, int probabilidad, struct Random randomGPU);
__device__ void mutacionPorInversionIVM(int* poblacion, int probabilidad, struct Random randomGPU);
__device__ void mutacionPorMezcla(int* poblacion, int probabilidad, struct Random randomGPU);
__device__ void mutacionUniforme(float* poblacion, int probabilidad, struct Random randomGPU);
__device__ void mutacionNoUniforme(float* poblacion, int probabilidad, struct Random randomGPU, int generacionActual);
__device__ int calcularFitnessBinaria(int* poblacion, int posicion);
__device__ int calcularFitnessEntera(int* poblacion, int posicion);
__device__ float calcularFitnessReal(float* poblacion, int posicion);
__device__ void cruzamientoRepresentacionBinaria(int* poblacionActual, int* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU, int ejecutarFuncion);
__device__ void cruzamientoRepresentacionEntera(int* poblacionActual, int* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU, int ejecutarFuncion);
__device__ void cruzamientoRepresentacionReal(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU, int ejecutarFuncion);
__device__ void cruzamientoCromosoma1PuntoBinaria(int* poblacionActual, int* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU);
__device__ void cruzamientoCromosoma2PuntosBinaria(int* poblacionActual, int* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU);
__device__ void cruzamientoCromosomaUniformeBinaria(int* poblacionActual, int* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU);
__device__ void cruzamientoCromosoma1Punto(int* poblacionActual, int* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU);
__device__ void cruzamientoCromosoma2Puntos(int* poblacionActual, int* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU);
__device__ void cruzamientoCromosomaUniforme(int* poblacionActual, int* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU);
__device__ void cruzamientoCromosomaPMX(int* poblacionActual, int* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU);
__device__ int estaPresente(int valor, int* nuevaPoblacion, int desde, int hasta);
__device__ void cruzamientoCromosomaOX(int* poblacionActual, int* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU);
__device__ int posicionPrimerLibre(int* poblacionActual, int posicionNuevoCromosoma);
__device__ void cruzamientoCromosomaCX(int* poblacionActual, int* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad,struct Random randomGPU);
__device__ void cruzamientoAritmetico(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU);
__device__ void cruzamientoAritmeticoIndividual(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU);
__device__ void cruzamientoAritmeticoSimple(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU);
__device__ void cruzamientoAritmeticoCompleto(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU);

__global__ void inicializarPoblacion(int* poblacion, int* fitnessActualDevice, struct Random randomGPU, int cantCromosomasPoblacion, int ejecutarFuncion){
	if(ejecutarFuncion == 0) {
		inicializarPoblacionBinaria(poblacion, fitnessActualDevice ,randomGPU, cantCromosomasPoblacion);
	}
	else{
		if(ejecutarFuncion == 1){
			inicializarPoblacionEntera(poblacion, fitnessActualDevice ,randomGPU, cantCromosomasPoblacion);
		}
		else {
			if(ejecutarFuncion == 2) {
				inicializarPoblacionPermutacionEntera(poblacion, fitnessActualDevice ,randomGPU, cantCromosomasPoblacion);
			}
		}
	}
}

__device__ void inicializarPoblacionBinaria(int* poblacion, int* fitnessActualDevice, struct Random randomGPU, int cantCromosomasPoblacion){
	//Obtengo posicion a escribir.
	int posicionCromosoma = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * totalGenesEnPoblacionDevice;
	int posicionFitness = threadIdx.y * 2 + blockIdx.y * cantCromosomasPoblacionDevice;
	for(int i = 0; i<cantGenesEnCromosomaDevice * 2; i++){
		//Genero valor aleatorio.
		unsigned int r = (getRandomInt(randomGPU, posicionCromosoma)% 2);
		poblacion[posicionCromosoma+i] = r;
	}
	fitnessActualDevice[posicionFitness] = calcularFitnessEntera(poblacion, posicionCromosoma);
	fitnessActualDevice[posicionFitness + 1] = calcularFitnessEntera(poblacion, posicionCromosoma + cantGenesEnCromosomaDevice);
}

__device__ void inicializarPoblacionEntera(int* poblacion, int* fitnessActualDevice, struct Random randomGPU, int cantCromosomasPoblacion){
	//Obtengo posicion a escribir.
	int posicionCromosoma = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * totalGenesEnPoblacionDevice;
	int posicionFitness = threadIdx.y * 2 + blockIdx.y * cantCromosomasPoblacionDevice;
	for(int i = 0; i<cantGenesEnCromosomaDevice * 2; i++){
		//Genero valor aleatorio.
		unsigned int r = (getRandomInt(randomGPU, posicionCromosoma)% 10);
		poblacion[posicionCromosoma+i] = r;
	}
	fitnessActualDevice[posicionFitness] = calcularFitnessEntera(poblacion, posicionCromosoma);
	fitnessActualDevice[posicionFitness + 1] = calcularFitnessEntera(poblacion, posicionCromosoma + cantGenesEnCromosomaDevice);
}

__device__ void inicializarPoblacionPermutacionEntera(int* poblacion, int* fitnessActualDevice, struct Random randomGPU, int cantCromosomasPoblacion){
	//Obtengo posicion a escribir.
	int posicionCromosoma = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * totalGenesEnPoblacionDevice;
	int posicionFitness = threadIdx.y * 2 + blockIdx.y * cantCromosomasPoblacionDevice;

	//inicializo la poblacion en -1
	for(int i = 0; i<cantGenesEnCromosomaDevice * 2; i++){
		poblacion[posicionCromosoma+i] = -1;
	}

	for(int i = 0; i<cantGenesEnCromosomaDevice; i++){
		//Genero valor aleatorio.
		unsigned int r = (getRandomInt(randomGPU, posicionCromosoma)% cantGenesEnCromosomaDevice);
		while(poblacion[posicionCromosoma+r] != -1){
			r = (r+1) % cantGenesEnCromosomaDevice;
		}
		poblacion[posicionCromosoma+r] = i;
	}

	for(int i = 0; i<cantGenesEnCromosomaDevice; i++){
		//Genero valor aleatorio.
		unsigned int r = (getRandomInt(randomGPU, posicionCromosoma)% cantGenesEnCromosomaDevice);
		while(poblacion[posicionCromosoma+cantGenesEnCromosomaDevice+r] != -1){
			r = (r+1) % cantGenesEnCromosomaDevice;
		}
		poblacion[posicionCromosoma+cantGenesEnCromosomaDevice+r] = i;
	}


	fitnessActualDevice[posicionFitness] = calcularFitnessEntera(poblacion, posicionCromosoma);
	fitnessActualDevice[posicionFitness + 1] = calcularFitnessEntera(poblacion, posicionCromosoma + cantGenesEnCromosomaDevice);
}

__global__ void inicializarPoblacionReal(float* poblacion, float* fitnessActualDevice, struct Random randomGPU, int cantCromosomasPoblacion, int ejecutarFuncion){
	//Obtengo posicion a escribir.
	int posicionCromosoma = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * totalGenesEnPoblacionDevice;
	int posicionFitness = threadIdx.y * 2 + blockIdx.y * cantCromosomasPoblacionDevice;
	for(int i = 0; i<cantGenesEnCromosomaDevice * 2; i++){
		//Genero valor aleatorio.
		float r = (getRandom(randomGPU, posicionCromosoma))/100;
		poblacion[posicionCromosoma+i] = r;
	}
	fitnessActualDevice[posicionFitness] = calcularFitnessReal(poblacion, posicionCromosoma);
	fitnessActualDevice[posicionFitness + 1] = calcularFitnessReal(poblacion, posicionCromosoma + cantGenesEnCromosomaDevice);

}

__global__ void mainKernelBinaria(int* poblacionActualDevice, int* nuevaPoblacionDevice, int* fitnessActualDevice, int* nuevoFitnessDevice, int* individuosAMigrarDevice, struct Random r){
	//genero numero aleatorio entre 0 y cant individuos por poblacion
	int posicionCromosoma1 = seleccionBinaria(fitnessActualDevice, r, tipoSeleccion, 1);
	int posicionCromosoma2 = seleccionBinaria(fitnessActualDevice, r, tipoSeleccion, 2);
	cruzamientoRepresentacionBinaria(poblacionActualDevice, nuevaPoblacionDevice, posicionCromosoma1, posicionCromosoma2, probabilidadCruzamiento, r, tipoCruzamiento);

	mutacionBinaria(nuevaPoblacionDevice, probabilidadMutacion, r, tipoMutacion);
	//asignarFitness(nuevaPoblacionDevice, nuevoFitnessDevice);

	//__syncthreads();
	//if (threadIdx.x == 0) {
	///	individosAMigrarRepresentacionBinaria(nuevoFitnessDevice, nuevaPoblacionDevice, individuosAMigrarDevice);
	//}
}


__global__ void mainKernelEntera(int* poblacionActualDevice, int* nuevaPoblacionDevice, int* fitnessActualDevice, int* nuevoFitnessDevice, int* individuosAMigrarDevice, struct Random r){
	//genero numero aleatorio entre 0 y cant individuos por poblacion
	int posicionCromosoma1 = seleccionEntera(fitnessActualDevice, r, tipoSeleccion, 1);
	int posicionCromosoma2 = seleccionEntera(fitnessActualDevice, r, tipoSeleccion, 2);
	cruzamientoRepresentacionEntera(poblacionActualDevice, nuevaPoblacionDevice, posicionCromosoma1, posicionCromosoma2, probabilidadCruzamiento, r, tipoCruzamiento);
	mutacionEntera(nuevaPoblacionDevice, probabilidadMutacion, r, tipoMutacion);
	
	asignarFitness(nuevaPoblacionDevice, nuevoFitnessDevice);

	//__syncthreads();
	//if (threadIdx.x == 0) {
	///	individosAMigrarRepresentacionBinaria(nuevoFitnessDevice, nuevaPoblacionDevice, individuosAMigrarDevice);
	//}
}

__global__ void mainKernelReal(float* poblacionActualDevice, float* nuevaPoblacionDevice, float* fitnessActualDevice, float* nuevoFitnessDevice, float* individuosAMigrarDevice, struct Random r, int generacionActual){
	//genero numero aleatorio entre 0 y cant individuos por poblacion
	int posicionCromosoma1 = seleccionReal(fitnessActualDevice, r, tipoSeleccion, 1);
	int posicionCromosoma2 = seleccionReal(fitnessActualDevice, r, tipoSeleccion, 2);
	cruzamientoRepresentacionReal(poblacionActualDevice, nuevaPoblacionDevice, posicionCromosoma1, posicionCromosoma2, probabilidadCruzamiento, r, tipoCruzamiento);

	mutacionReal(nuevaPoblacionDevice, probabilidadMutacion, r, tipoMutacion, generacionActual);
	//asignarFitness(nuevaPoblacionDevice, nuevoFitnessDevice);

	//__syncthreads();
	//if (threadIdx.x == 0) {
	///	individosAMigrarRepresentacionBinaria(nuevoFitnessDevice, nuevaPoblacionDevice, individuosAMigrarDevice);
	//}
}

/******************* Implementacion de Funciones de Migracion *****************/

__global__ void migracionRepresentacionBinaria(int* poblacionActualDevice, int* fitnessActualDevice, int* individuosAMigrarDevice, struct Random randomGPU){
	unsigned int r = getRandomInt(randomGPU, threadIdx.y)% cantCromosomasPoblacionDevice;
	int posicionCromosomaEmigranteAleatoria = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + r * cantGenesEnCromosomaDevice;
	int posicionCromosomaInmigrante = ((blockIdx.y + 1) % cantidadPoblacionesDevice )* cantGenesEnCromosomaDevice;
	for (int i = 0; i<cantGenesEnCromosomaDevice; i++) {
		poblacionActualDevice[posicionCromosomaEmigranteAleatoria + i] = individuosAMigrarDevice[posicionCromosomaInmigrante + i];
	}
	int posicionFitness = blockIdx.y * cantCromosomasPoblacionDevice;
	fitnessActualDevice[posicionFitness] = calcularFitnessEntera(poblacionActualDevice, posicionCromosomaEmigranteAleatoria);

	//cuPrintf("posicionCromosomaEmigranteAleatoria = %d \n", posicionCromosomaEmigranteAleatoria);
	//cuPrintf("posicionCromosomaInmigrante = %d \n", posicionCromosomaInmigrante);

}

__device__ void individosAMigrarRepresentacionBinaria(int* nuevoFitnessDevice, int* nuevaPoblacionDevice, int* individuosAMigrarDevice){
	int posicionMejorIndividuo = seleccionElitistaEntera(nuevoFitnessDevice, 1);
	for (int i = 0; i<cantGenesEnCromosomaDevice; i++) {
		individuosAMigrarDevice[blockIdx.y*cantGenesEnCromosomaDevice + i] = nuevaPoblacionDevice[posicionMejorIndividuo + i];
	}
}


/******************* Implementacion de Funciones de Fitness *****************/

__device__ void asignarFitness(int* nuevaPoblacion, int* nuevoVectorFitness){
	//Obtengo posicion del primer cromosoma.
	int posicionCromosoma = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * totalGenesEnPoblacionDevice;
	int posicionFitness = threadIdx.y * 2 + blockIdx.y * cantCromosomasPoblacionDevice;
	nuevoVectorFitness[posicionFitness] = calcularFitnessEntera(nuevaPoblacion, posicionCromosoma);
	nuevoVectorFitness[posicionFitness + 1] = calcularFitnessEntera(nuevaPoblacion, posicionCromosoma + cantGenesEnCromosomaDevice);
}


/******************* Implementacion de Funciones de Selecccion *****************/

__device__ int seleccionBinaria(int* vectorFitness, struct Random randomGPU, int ejecutarFuncion, int indiceMejor){
	int resultado = -1;
	if(ejecutarFuncion == 0) {
		resultado = seleccionElitistaBinaria(vectorFitness, indiceMejor);
	}
	else{
		if(ejecutarFuncion == 1){
			resultado = seleccionPorTorneoBinaria(vectorFitness, randomGPU);
		}
		/*else {
			if(ejecutarFuncion == 2) {
				//seleccionElitistaBinaria(vectorFitness, randomGPU);
			}
		}*/
	}
	return resultado;
}

__device__ int seleccionEntera(int* vectorFitness, struct Random randomGPU, int ejecutarFuncion, int indiceMejor){
	int resultado = -1;
	if(ejecutarFuncion == 0) {
		resultado = seleccionElitistaEntera(vectorFitness, indiceMejor);
	}
	else{
		if(ejecutarFuncion == 1){
			resultado = seleccionPorTorneoEntera(vectorFitness, randomGPU);
		}
		/*else {
			if(ejecutarFuncion == 2) {
				//seleccionElitistaEntera(vectorFitness, randomGPU);
			}
		}*/
	}
	return resultado;
}

__device__ int seleccionReal(float* vectorFitness, struct Random randomGPU, int ejecutarFuncion, int indiceMejor){
	int resultado = -1;
	if(ejecutarFuncion == 0) {
		resultado = seleccionElitistaReal(vectorFitness, indiceMejor);
	}
	else if(ejecutarFuncion == 1) {
		resultado = seleccionPorTorneoReal(vectorFitness, randomGPU);
	}
	return resultado;
}


/******************* Implementacion de Funciones de Selecccion BINARIA *****************/

//Seleccion elitista
//selecciona el mejor o segundo mejor elemento segun se indica en indiceMejor con un 1 o con un 2
__device__ int seleccionElitistaBinaria(int* vectorFitness, int indiceMejor){
	cuPrintf("*** Seleccion elitista ***\n");
	int mejor1 = -1;
	int mejor2 = -2;
	int indiceMejor1, indiceMejor2;
	int valorFitness;
	int indiceMemoria = blockIdx.y * cantCromosomasPoblacionDevice;
	for(int i = 0; i< cantCromosomasPoblacionDevice; i++){
		valorFitness = vectorFitness[indiceMemoria + i];
		if (valorFitness > mejor1) {
			mejor2 = mejor1;
			indiceMejor2 = indiceMejor1;
			mejor1 = valorFitness;
			indiceMejor1 = i;
		}
		else {
			if (valorFitness > mejor2){
				mejor2 = valorFitness;
				indiceMejor2 = i;
			}
		}
	}
	int posicionCromosomaMejor1 = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + indiceMejor1 * cantGenesEnCromosomaDevice;
	int posicionCromosomaMejor2 = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + indiceMejor2 * cantGenesEnCromosomaDevice;
	if (indiceMejor == 1) {
		return posicionCromosomaMejor1;
	}
	return posicionCromosomaMejor2;
}

//Seleccion por torneo
//selecciona 2 individuos al azar y devuelve la posicion del mejor
__device__ int seleccionPorTorneoBinaria(int* vectorFitness, struct Random randomGPU){
	cuPrintf("*** Seleccion por torneo ***\n");
	unsigned int r = getRandomInt(randomGPU, threadIdx.y)% cantCromosomasPoblacionDevice;
	int posicionFitnessAleatoria1 = blockIdx.y * cantCromosomasPoblacionDevice + r;
	int posicionCromosomaAleatoria1 = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + r * cantGenesEnCromosomaDevice;
	r = getRandomInt(randomGPU, threadIdx.y + 1)% cantCromosomasPoblacionDevice;
	int posicionFitnessAleatoria2 = blockIdx.y * cantCromosomasPoblacionDevice + r;
	int posicionCromosomaAleatoria2 = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + r * cantGenesEnCromosomaDevice;
	if (vectorFitness[posicionFitnessAleatoria1] > vectorFitness[posicionFitnessAleatoria2])
		return posicionCromosomaAleatoria1;
	return posicionCromosomaAleatoria2;
}

/******************* Implementacion de Funciones de Selecccion ENTERA y PERMUTACION *****************/

//Seleccion elitista
//selecciona el mejor o segundo mejor elemento segun se indica en indiceMejor con un 1 o con un 2
__device__ int seleccionElitistaEntera(int* vectorFitness, int indiceMejor){
	cuPrintf("*** Seleccion elitista ***\n");
	int mejor1 = -1;
	int mejor2 = -2;
	int indiceMejor1, indiceMejor2;
	int valorFitness;
	int indiceMemoria = blockIdx.y * cantCromosomasPoblacionDevice;
	for(int i = 0; i< cantCromosomasPoblacionDevice; i++){
		valorFitness = vectorFitness[indiceMemoria + i];
		if (valorFitness > mejor1) {
			mejor2 = mejor1;
			indiceMejor2 = indiceMejor1;
			mejor1 = valorFitness;
			indiceMejor1 = i;
		}
		else {
			if (valorFitness > mejor2){
				mejor2 = valorFitness;
				indiceMejor2 = i;
			}
		}
	}

	//cuPrintf("indiceMejor1 = %d \n", indiceMejor1);
	//cuPrintf("indiceMejor2 = %d \n", indiceMejor2);

	int posicionCromosomaMejor1 = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + indiceMejor1 * cantGenesEnCromosomaDevice;
	int posicionCromosomaMejor2 = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + indiceMejor2 * cantGenesEnCromosomaDevice;
	//cuPrintf("posicionCromosomaMejor1 = %d \n", posicionCromosomaMejor1);
	//cuPrintf("posicionCromosomaMejor2 = %d \n", posicionCromosomaMejor2);

	if (indiceMejor == 1) {
		return posicionCromosomaMejor1;
	}
	return posicionCromosomaMejor2;

}

//Seleccion por torneo
//selecciona 2 individuos al azar y devuelve la posicion del mejor
__device__ int seleccionPorTorneoEntera(int* vectorFitness, struct Random randomGPU){
	cuPrintf("*** Seleccion por torneo ***\n");
	unsigned int r = getRandomInt(randomGPU, threadIdx.y)% cantCromosomasPoblacionDevice;
	int posicionFitnessAleatoria1 = blockIdx.y * cantCromosomasPoblacionDevice + r;
	int posicionCromosomaAleatoria1 = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + r * cantGenesEnCromosomaDevice;
	r = getRandomInt(randomGPU, threadIdx.y + 1)% cantCromosomasPoblacionDevice;
	int posicionFitnessAleatoria2 = blockIdx.y * cantCromosomasPoblacionDevice + r;
	int posicionCromosomaAleatoria2 = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + r * cantGenesEnCromosomaDevice;
	if (vectorFitness[posicionFitnessAleatoria1] > vectorFitness[posicionFitnessAleatoria2])
		return posicionCromosomaAleatoria1;
	return posicionCromosomaAleatoria2;
}

//Seleccion por rango
//Se ordenan los individuos según su fitness y la selección se realiza mediante este ranking.
__device__ int seleccionPorRangoEntera(int* vectorFitness, int indiceMejor){
	return 0;

}

/******************* Implementacion de Funciones de Selecccion REAL *****************/

//Seleccion elitista
//selecciona el mejor o segundo mejor elemento segun se indica en indiceMejor con un 1 o con un 2
__device__ int seleccionElitistaReal(float* vectorFitness, int indiceMejor){
	cuPrintf("*** Seleccion elitista real ***\n");
	int mejor1 = -1;
	int mejor2 = -2;
	int indiceMejor1, indiceMejor2;
	float valorFitness;
	int indiceMemoria = blockIdx.y * cantCromosomasPoblacionDevice;
	for(int i = 0; i< cantCromosomasPoblacionDevice; i++){
		valorFitness = vectorFitness[indiceMemoria + i];
		if (valorFitness > mejor1) {
			mejor2 = mejor1;
			indiceMejor2 = indiceMejor1;
			mejor1 = valorFitness;
			indiceMejor1 = i;
		}
		else {
			if (valorFitness > mejor2){
				mejor2 = valorFitness;
				indiceMejor2 = i;
			}
		}
	}
	int posicionCromosomaMejor1 = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + indiceMejor1 * cantGenesEnCromosomaDevice;
	int posicionCromosomaMejor2 = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + indiceMejor2 * cantGenesEnCromosomaDevice;
	if (indiceMejor == 1) {
		return posicionCromosomaMejor1;
	}
	return posicionCromosomaMejor2;
}

//Seleccion por torneo
//selecciona 2 individuos al azar y devuelve la posicion del mejor
__device__ int seleccionPorTorneoReal(float* vectorFitness, struct Random randomGPU){
	cuPrintf("*** Seleccion por torneo real ***\n");
	unsigned int r = getRandomInt(randomGPU, threadIdx.y)% cantCromosomasPoblacionDevice;
	int posicionFitnessAleatoria1 = blockIdx.y * cantCromosomasPoblacionDevice + r;
	int posicionCromosomaAleatoria1 = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + r * cantGenesEnCromosomaDevice;
	r = getRandomInt(randomGPU, threadIdx.y + 1)% cantCromosomasPoblacionDevice;
	int posicionFitnessAleatoria2 = blockIdx.y * cantCromosomasPoblacionDevice + r;
	int posicionCromosomaAleatoria2 = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + r * cantGenesEnCromosomaDevice;
	if (vectorFitness[posicionFitnessAleatoria1] > vectorFitness[posicionFitnessAleatoria2])
		return posicionCromosomaAleatoria1;
	return posicionCromosomaAleatoria2;
}

/******************* Implementacion de Funciones de Cruzamiento *****************/

/******************* Implementacion de Funciones de Cruzamiento BINARIA y ENTERA *****************/

__device__ void cruzamientoRepresentacionBinaria(int* poblacionActual, int* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU, int ejecutarFuncion){

	if(ejecutarFuncion == 0) {
		cruzamientoCromosoma1PuntoBinaria(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, randomGPU);
	}
	else{
		if(ejecutarFuncion == 1){
			cruzamientoCromosoma2PuntosBinaria(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, randomGPU);
		}
		else {
			if(ejecutarFuncion == 2) {
				cruzamientoCromosomaUniformeBinaria(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, randomGPU);
			}
		}
	}
}


__device__ void cruzamientoRepresentacionEntera(int* poblacionActual, int* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU, int ejecutarFuncion){

	if(ejecutarFuncion == 0) {
		cruzamientoCromosoma1Punto(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, randomGPU);
	}
	else{
		if(ejecutarFuncion == 1){
			cruzamientoCromosoma2Puntos(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, randomGPU);
		}
		else {
			if(ejecutarFuncion == 2) {
				cruzamientoCromosomaUniforme(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, randomGPU);
			}
			else {
				if(ejecutarFuncion == 3) {
					cruzamientoCromosomaPMX(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, randomGPU);
				}
				else {
					if(ejecutarFuncion == 4) {
						cruzamientoCromosomaOX(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, randomGPU);
					}
					else {
						if(ejecutarFuncion == 5) {
							cruzamientoCromosomaCX(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, randomGPU);
						}
					}
				}
			}

		}
	}
}


__device__ void cruzamientoRepresentacionReal(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU, int ejecutarFuncion){

	if(ejecutarFuncion == 0) {
		cruzamientoAritmetico(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, randomGPU);
	}
	else{
		if(ejecutarFuncion == 1){
			cruzamientoAritmeticoIndividual(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, randomGPU);
		}
		else {
			if(ejecutarFuncion == 2) {
				cruzamientoAritmeticoSimple(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, randomGPU);
			}
			else {
				if(ejecutarFuncion == 3) {
					cruzamientoAritmeticoCompleto(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, randomGPU);
				}
			}
		}
	}
}



//////////////// Cruzamiento representacion binaria /////////////////////
//Se selecciona un punto de corte y se intercambian los genes de los padres para formar los hijos
__device__ void cruzamientoCromosoma1PuntoBinaria(int* poblacionActual, int* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU){
	cuPrintf("*** Cruzamiento 1Punto ***\n");
	int posicionNuevoCromosoma1 = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * totalGenesEnPoblacionDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosomaDevice;
	//cuPrintf("posicionCromosoma1  %d\n", posicionCromosoma1);
	//cuPrintf("posicionCromosoma2  %d\n", posicionCromosoma2);

	if((getRandomInt(randomGPU, posicionNuevoCromosoma1) % 101) >= probabilidad ){
		unsigned int r = getRandomInt(randomGPU, posicionNuevoCromosoma1) % cantGenesEnCromosomaDevice;
		cuPrintf("r %d\n", r);
		for(int i=0; i<cantGenesEnCromosomaDevice; i++){
			if(i<=r){
				nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma1 + i];
				nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma2 + i];
			}else{
				nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma2 + i];
				nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma1 + i];
			}
		}
	}else{
		for(int i=0; i<cantGenesEnCromosomaDevice; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma1 + i];
			nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma2 + i];
		}
	}
}

//Se seleccionan 2 puntos de corte y se intercambian los genes de los padres para formar los hijos
__device__ void cruzamientoCromosoma2PuntosBinaria(int* poblacionActual, int* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU){
	cuPrintf("*** Cruzamiento 2Puntos ***\n");
	int posicionNuevoCromosoma1 = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * totalGenesEnPoblacionDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosomaDevice;
	unsigned int prob = (getRandomInt(randomGPU, posicionNuevoCromosoma1) % 101);
	if(prob < probabilidad ){
		unsigned int r1 = getRandomInt(randomGPU, posicionNuevoCromosoma1) % cantGenesEnCromosomaDevice;
		unsigned int r2 = getRandomInt(randomGPU, posicionNuevoCromosoma2) % cantGenesEnCromosomaDevice;
		cuPrintf("r1 %d\n", r1);
		cuPrintf("r2 %d\n", r2);

		int i = 0;
		while(r2 == r1){
			r2 = getRandomInt(randomGPU, posicionNuevoCromosoma2+i) % cantGenesEnCromosomaDevice;
			i++;
		}

		if(r2<r1){
			unsigned int r3 = r1;
			r1 = r2;
			r2 = r3;
		}

		for(int i=0; i<cantGenesEnCromosomaDevice; i++){
			if(i<r1){
				nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma1 + i];
				nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma2 + i];
			}else if(i<=r2){
				nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma2 + i];
				nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma1 + i];
			}else{
				nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma1 + i];
				nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma2 + i];
			}
		}
	}else{
		for(int i=0; i<cantGenesEnCromosomaDevice; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma1 + i];
			nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma2 + i];
		}
	}
}


//en cada posición de los progenitores se decide intercambiar el valor
//genético de cada punto de acuerdo a una probabilidad prefijada
__device__ void cruzamientoCromosomaUniformeBinaria(int* poblacionActual, int* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU){
	cuPrintf("*** Cruzamiento Uniforme ***\n");
	int posicionNuevoCromosoma1 = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * totalGenesEnPoblacionDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosomaDevice;

	unsigned int prob;
	for(int i=0; i<cantGenesEnCromosomaDevice; i++){
		prob = (getRandomInt(randomGPU, posicionNuevoCromosoma1 + i) % 101);
		if(prob < probabilidad ){
			cuPrintf("i = %d", i);
			nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma2 + i];
			nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma1 + i];
		}
		else {
			nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma1 + i];
			nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma2 + i];
		}
	}
}

/******************* Implementacion de Funciones de Cruzamiento ENTERA y PERMUTACION *****************/

//Se selecciona un punto de corte y se intercambian los genes de los padres para formar los hijos
__device__ void cruzamientoCromosoma1Punto(int* poblacionActual, int* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU){
	cuPrintf("*** Cruzamiento 1Punto ***\n");
	int posicionNuevoCromosoma1 = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * totalGenesEnPoblacionDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosomaDevice;
	//cuPrintf("posicionCromosoma1  %d\n", posicionCromosoma1);
	//cuPrintf("posicionCromosoma2  %d\n", posicionCromosoma2);

	if((getRandomInt(randomGPU, posicionNuevoCromosoma1) % 101) >= probabilidad ){
		unsigned int r = getRandomInt(randomGPU, posicionNuevoCromosoma1) % cantGenesEnCromosomaDevice;
		cuPrintf("r %d\n", r);
		for(int i=0; i<cantGenesEnCromosomaDevice; i++){
			if(i<=r){
				nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma1 + i];
				nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma2 + i];
			}else{
				nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma2 + i];
				nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma1 + i];
			}
		}
	}else{
		for(int i=0; i<cantGenesEnCromosomaDevice; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma1 + i];
			nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma2 + i];
		}
	}
}

//Se seleccionan 2 puntos de corte y se intercambian los genes de los padres para formar los hijos
__device__ void cruzamientoCromosoma2Puntos(int* poblacionActual, int* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU){
	cuPrintf("*** Cruzamiento 2Puntos ***\n");
	int posicionNuevoCromosoma1 = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * totalGenesEnPoblacionDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosomaDevice;
	unsigned int prob = (getRandomInt(randomGPU, posicionNuevoCromosoma1) % 101);
	if(prob < probabilidad ){
		unsigned int r1 = getRandomInt(randomGPU, posicionNuevoCromosoma1) % cantGenesEnCromosomaDevice;
		unsigned int r2 = getRandomInt(randomGPU, posicionNuevoCromosoma2) % cantGenesEnCromosomaDevice;
		cuPrintf("r1 %d\n", r1);
		cuPrintf("r2 %d\n", r2);

		int i = 0;
		while(r2 == r1){
			r2 = getRandomInt(randomGPU, posicionNuevoCromosoma2+i) % cantGenesEnCromosomaDevice;
			i++;
		}

		if(r2<r1){
			unsigned int r3 = r1;
			r1 = r2;
			r2 = r3;
		}

		for(int i=0; i<cantGenesEnCromosomaDevice; i++){
			if(i<r1){
				nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma1 + i];
				nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma2 + i];
			}else if(i<=r2){
				nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma2 + i];
				nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma1 + i];
			}else{
				nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma1 + i];
				nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma2 + i];
			}
		}
	}else{
		for(int i=0; i<cantGenesEnCromosomaDevice; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma1 + i];
			nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma2 + i];
		}
	}
}


//en cada posición de los progenitores se decide intercambiar el valor
//genético de cada punto de acuerdo a una probabilidad prefijada
__device__ void cruzamientoCromosomaUniforme(int* poblacionActual, int* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU){
	cuPrintf("*** Cruzamiento Uniforme ***\n");
	int posicionNuevoCromosoma1 = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * totalGenesEnPoblacionDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosomaDevice;

	unsigned int prob;
	for(int i=0; i<cantGenesEnCromosomaDevice; i++){
		prob = (getRandomInt(randomGPU, posicionNuevoCromosoma1 + i) % 101);
		if(prob < probabilidad ){
			nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma2 + i];
			nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma1 + i];
		}
		else {
			nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma1 + i];
			nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma2 + i];
		}
	}
}

__device__ int dondeEsta(int* poblacionActual, int valor, int posicionNuevoCromosoma){

	for(int i=0; i<cantGenesEnCromosomaDevice; i++){
		if(poblacionActual[posicionNuevoCromosoma+i]==valor)
			return i;
	}
	return 0;
}

__device__ void cruzamientoCromosomaPMX(int* poblacionActual, int* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU){
	cuPrintf("*** Cruzamiento PMX ***\n");
	int posicionNuevoCromosoma1 = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * totalGenesEnPoblacionDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosomaDevice;

	unsigned int prob;
	prob = (getRandomInt(randomGPU, posicionNuevoCromosoma1) % 101);
	if(prob < probabilidad ){
		unsigned int r1 = getRandomInt(randomGPU, posicionNuevoCromosoma1) % cantGenesEnCromosomaDevice;
		unsigned int r2 = getRandomInt(randomGPU, posicionNuevoCromosoma2) % cantGenesEnCromosomaDevice;
		//cuPrintf("r1 %d\n", r1);
		//cuPrintf("r2 %d\n", r2);

		int i = 0;
		while(r2 == r1){
			r2 = getRandomInt(randomGPU, posicionNuevoCromosoma2+i) % cantGenesEnCromosomaDevice;
			i++;
		}

		if(r2<r1){
			unsigned int r3 = r1;
			r1 = r2;
			r2 = r3;
		}

		for(int j=0; j<cantGenesEnCromosomaDevice; j++){
			if((j>=r1) && (j <= r2)){
				nuevaPoblacion[posicionNuevoCromosoma1+j] = poblacionActual[posicionCromosoma1+j];
				nuevaPoblacion[posicionNuevoCromosoma2+j] = poblacionActual[posicionCromosoma2+j];
			}else{
				nuevaPoblacion[posicionNuevoCromosoma1+j] = -1;
				nuevaPoblacion[posicionNuevoCromosoma2+j] = -1;
			}
		}
		int valor, valorInicial;
		for(int k=r1; k<=r2; k++){
			cuPrintf("k %d\n", k);
			valor = poblacionActual[posicionCromosoma2+k];
			valorInicial = valor;
			int pos = dondeEsta(poblacionActual,valor,posicionCromosoma1);

			if ((pos<r1) || (pos>r2)) {
				valor = poblacionActual[posicionCromosoma1+pos];
				pos = dondeEsta(poblacionActual,valor,posicionCromosoma2);
				cuPrintf("valor %d pos %d\n", valor, pos);
				while ((pos>=r1) && (pos<=r2)) {
					valor = poblacionActual[posicionCromosoma1+pos];
					pos = dondeEsta(poblacionActual,valor,posicionCromosoma2);
				}
				nuevaPoblacion[posicionNuevoCromosoma1+pos] = valorInicial;
			}
		}
		for(int k=r1; k<=r2; k++){
			valor = poblacionActual[posicionCromosoma1+k];
			valorInicial = valor;
			int pos = dondeEsta(poblacionActual,valor,posicionCromosoma2);
			if ((pos<r1) || (pos > r2)) {
				valor = poblacionActual[posicionCromosoma2+pos];
				pos = dondeEsta(poblacionActual,valor,posicionCromosoma1);

				while ((pos>=r1) && (pos <= r2)) {
					valor = poblacionActual[posicionCromosoma2+pos];
					pos = dondeEsta(poblacionActual,valor,posicionCromosoma1);
				}

				nuevaPoblacion[posicionNuevoCromosoma2+pos] = valorInicial;
			}
		}
		for(int i=0; i<cantGenesEnCromosomaDevice; i++){
			if(nuevaPoblacion[posicionNuevoCromosoma1+i] == -1)
				nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma2+i];
			if(nuevaPoblacion[posicionNuevoCromosoma2+i] == -1)
				nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma1+i];
		}
	}
	else{
		for(int i=0; i<cantGenesEnCromosomaDevice; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma1 + i];
			nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma2 + i];
		}
	}
}

__device__ int estaPresente(int valor, int* nuevaPoblacion, int desde, int hasta){
	for (int i = desde; i< hasta; i++) {
		if (nuevaPoblacion[i] == valor)
			return 1;
	}
	return 0;
}

__device__ void cruzamientoCromosomaOX(int* poblacionActual, int* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU){
	cuPrintf("*** Cruzamiento OX ***\n");
	int posicionNuevoCromosoma1 = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * totalGenesEnPoblacionDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosomaDevice;

	unsigned int prob = (getRandomInt(randomGPU, posicionNuevoCromosoma1) % 101);
	if(prob < probabilidad ){
		unsigned int r1 = getRandomInt(randomGPU, posicionNuevoCromosoma1) % cantGenesEnCromosomaDevice;
		unsigned int r2 = getRandomInt(randomGPU, posicionNuevoCromosoma2) % cantGenesEnCromosomaDevice;
		cuPrintf("r1 %d\n", r1);
		cuPrintf("r2 %d\n", r2);

		int i = 0;
		while(r2 == r1){
			r2 = getRandomInt(randomGPU, posicionNuevoCromosoma2+i) % cantGenesEnCromosomaDevice;
			i++;
		}

		if(r2<r1){
			unsigned int r3 = r1;
			r1 = r2;
			r2 = r3;
		}

		for (int i=0; i<=cantGenesEnCromosomaDevice; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i] = -1;
			nuevaPoblacion[posicionNuevoCromosoma2+i] = -1;
		}

		int cont1 = 0;
	    int cont2 = 0;
	    for (int i=r1; i<=r2; i++){
	    	nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma1+i];
		    cont1++;

		    nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma2+i];
		    cont2++;
	    }

	    int j = (r2+1) % cantGenesEnCromosomaDevice;
		int k = (r2+1) % cantGenesEnCromosomaDevice;

		while (cont1 < cantGenesEnCromosomaDevice) {
			//if (estaPresente(poblacionActual[posicionCromosoma2 + j], nuevaPoblacion, posicionCromosoma1) == 0) {
			if (estaPresente(poblacionActual[posicionCromosoma2 + j], nuevaPoblacion, posicionCromosoma1, posicionCromosoma1 + cantGenesEnCromosomaDevice) == 0) {
				nuevaPoblacion[posicionNuevoCromosoma1+k] = poblacionActual[posicionCromosoma2 + j];
				cont1++;
				k = (k+1) % cantGenesEnCromosomaDevice;
			}
			j = (j+1) % cantGenesEnCromosomaDevice;
		}

		j = (r2+1) % cantGenesEnCromosomaDevice;
		k = (r2+1) % cantGenesEnCromosomaDevice;
		while (cont2 < cantGenesEnCromosomaDevice) {
			//if (estaPresente(poblacionActual[posicionCromosoma1 + j], nuevaPoblacion, posicionCromosoma2) == 0) {
			if (estaPresente(poblacionActual[posicionCromosoma1 + j], nuevaPoblacion, posicionCromosoma2, posicionCromosoma2 + cantGenesEnCromosomaDevice) == 0) {
				nuevaPoblacion[posicionNuevoCromosoma2+k] = poblacionActual[posicionCromosoma1 + j];
				cont2++;
				k = (k+1) % cantGenesEnCromosomaDevice;
			}
			j = (j+1) % cantGenesEnCromosomaDevice;
		}

	}
	else{
		for(int i=0; i<cantGenesEnCromosomaDevice; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma1 + i];
			nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma2 + i];
		}
	}
}

__device__ int posicionPrimerLibre(int* poblacionActual, int posicionNuevoCromosoma){
	for(int i=0; i<cantGenesEnCromosomaDevice; i++){
		if(poblacionActual[posicionNuevoCromosoma+i]==-1)
			return i;
	}
	return -1;
}


__device__ void cruzamientoCromosomaCX(int* poblacionActual, int* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad,struct Random randomGPU){
	cuPrintf("*** Cruzamiento CX ***\n");
	int posicionNuevoCromosoma1 = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * totalGenesEnPoblacionDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosomaDevice;
	cuPrintf("posicionCromosoma1  %d\n", posicionCromosoma1);
	cuPrintf("posicionCromosoma2  %d\n", posicionCromosoma2);

	int pos, pos2;

	unsigned int prob = (getRandomInt(randomGPU, posicionNuevoCromosoma1) % 101);
	if (prob < probabilidad){
		for(int i=0; i<cantGenesEnCromosomaDevice; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i] = -1;
			nuevaPoblacion[posicionNuevoCromosoma2+i] = -1;
		}
		int cont = 0;
		pos = posicionPrimerLibre(nuevaPoblacion, posicionCromosoma1);
		while (pos != -1) {
			cuPrintf("pos  %d\n", pos);

			if (cont == 0) {
				while(nuevaPoblacion[posicionCromosoma1 + pos] == -1) {
					nuevaPoblacion[posicionCromosoma1 + pos] = poblacionActual[posicionCromosoma1 + pos];
					pos2 = dondeEsta(poblacionActual, poblacionActual[posicionCromosoma1 + pos], posicionCromosoma2);
					nuevaPoblacion[posicionCromosoma2 + pos2] = poblacionActual[posicionCromosoma1 + pos];
					pos = pos2;
					cuPrintf("pos  %d\n", pos);
				}
			}
			else {
				while(nuevaPoblacion[posicionCromosoma2 + pos] == -1) {
					nuevaPoblacion[posicionCromosoma2 + pos] = poblacionActual[posicionCromosoma1 + pos];
					pos2 = dondeEsta(poblacionActual, poblacionActual[posicionCromosoma1 + pos], posicionCromosoma2);
					nuevaPoblacion[posicionCromosoma1 + pos2] = poblacionActual[posicionCromosoma1 + pos];
					pos = pos2;
					cuPrintf("pos  %d\n", pos);
				}
			}
			cont = (cont +1) % 2;
			cuPrintf("cont  %d\n", cont);
			pos = posicionPrimerLibre(nuevaPoblacion, posicionCromosoma1);
		}
	}
	else{
		for(int i=0; i<cantGenesEnCromosomaDevice; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma1 + i];
			nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma2 + i];
		}
	}
}

/******************* Implementacion de Funciones de Cruzamiento Real *****************/

__device__ void cruzamientoAritmetico(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU){
	cuPrintf("*** Cruzamiento Aritmetico ***\n");
	int posicionNuevoCromosoma1 = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * totalGenesEnPoblacionDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosomaDevice;

	unsigned int prob;
	float alfa = fmod(getRandom(randomGPU, posicionNuevoCromosoma1), 1);
	cuPrintf("alfa = %f", alfa);
	prob = (getRandomInt(randomGPU, posicionNuevoCromosoma1) % 101);
	if(prob < probabilidad ){
		for(int i=0; i<cantGenesEnCromosomaDevice; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i] = alfa * poblacionActual[posicionCromosoma1 + i] + (1-alfa) * poblacionActual[posicionCromosoma2 + i];
			nuevaPoblacion[posicionNuevoCromosoma2+i] = alfa * poblacionActual[posicionCromosoma2 + i] + (1-alfa) * poblacionActual[posicionCromosoma1 + i];
		}
	}else{
		for(int i=0; i<cantGenesEnCromosomaDevice; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma1 + i];
			nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma2 + i];
		}
	}
}

__device__ void cruzamientoAritmeticoIndividual(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU){
	cuPrintf("*** Cruzamiento Aritmetico Individual ***\n");
	int posicionNuevoCromosoma1 = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * totalGenesEnPoblacionDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosomaDevice;

	unsigned int prob;
	int genAcruzar = (getRandomInt(randomGPU, posicionNuevoCromosoma1) % cantGenesEnCromosomaDevice);
	prob = (getRandomInt(randomGPU, posicionNuevoCromosoma1) % 101);
	for(int i=0; i<cantGenesEnCromosomaDevice; i++){
		nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma1 + i];
		nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma2 + i];
		if((i == genAcruzar) && (prob < probabilidad)){
			float alfa = fmod(getRandom(randomGPU, posicionNuevoCromosoma1 + i), 1);
			cuPrintf("alfa = %f", alfa);
			nuevaPoblacion[posicionNuevoCromosoma1+i] = alfa * poblacionActual[posicionCromosoma1 + i] + (1-alfa) * poblacionActual[posicionCromosoma2 + i];
			nuevaPoblacion[posicionNuevoCromosoma2+i] = alfa * poblacionActual[posicionCromosoma2 + i] + (1-alfa) * poblacionActual[posicionCromosoma1 + i];
		}
	}
}

__device__ void cruzamientoAritmeticoSimple(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU){
	cuPrintf("*** Cruzamiento Aritmetico Simple ***\n");
	int posicionNuevoCromosoma1 = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * totalGenesEnPoblacionDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosomaDevice;

	unsigned int prob;
	unsigned int k = (getRandomInt(randomGPU, posicionNuevoCromosoma1) % cantGenesEnCromosomaDevice);
	cuPrintf("k = %d", k);
	float alfa = fmod(getRandom(randomGPU, posicionNuevoCromosoma1), 1);
	cuPrintf(" alfa = %f\n", alfa);
	for(int i=0; i<k; i++){
		nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma1 + i];
		nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma2 + i];
	}
	prob = (getRandomInt(randomGPU, posicionNuevoCromosoma1) % 101);
	if(prob < probabilidad ){
		for(int i=k; i<cantGenesEnCromosomaDevice; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i] = alfa * poblacionActual[posicionCromosoma2 + i] + (1-alfa) * poblacionActual[posicionCromosoma1 + i];
			nuevaPoblacion[posicionNuevoCromosoma2+i] = alfa * poblacionActual[posicionCromosoma1 + i] + (1-alfa) * poblacionActual[posicionCromosoma2 + i];
		}
	}else{
		for(int i=k; i<cantGenesEnCromosomaDevice; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma1 + i];
			nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma2 + i];
		}
	}
}

__device__ void cruzamientoAritmeticoCompleto(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU){
	cuPrintf("*** Cruzamiento Aritmetico Completo ***\n");
	int posicionNuevoCromosoma1 = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * totalGenesEnPoblacionDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosomaDevice;

	unsigned int prob = (getRandomInt(randomGPU, posicionNuevoCromosoma1) % 101);
	float alfa = fmod(getRandom(randomGPU, posicionNuevoCromosoma1), 1);
	cuPrintf(" alfa = %f\n", alfa);
	if(prob < probabilidad ){
		for(int i=0; i<cantGenesEnCromosomaDevice; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i] = alfa * poblacionActual[posicionCromosoma1 + i] + (1-alfa) * poblacionActual[posicionCromosoma2 + i];
			nuevaPoblacion[posicionNuevoCromosoma2+i] = alfa * poblacionActual[posicionCromosoma2 + i] + (1-alfa) * poblacionActual[posicionCromosoma1 + i];
		}
	}else{
		for(int i=0; i<cantGenesEnCromosomaDevice; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma1 + i];
			nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma2 + i];
		}
	}
}

/******************* Implementacion de Funciones de Mutacion *****************/

__device__ void mutacionBinaria(int* poblacion, int probabilidad, struct Random randomGPU, int ejecutarFuncion){
	if(ejecutarFuncion == 0) {
		mutacionPorInversion(poblacion, probabilidad, randomGPU);
	}
}

__device__ void mutacionEntera(int* poblacion, int probabilidad, struct Random randomGPU, int ejecutarFuncion){

	if(ejecutarFuncion == 0){
		mutacionReajusteAleatorio(poblacion, probabilidad, randomGPU);
	}
	else {
		if(ejecutarFuncion == 1) {
			mutacionPorDeslizamiento(poblacion, probabilidad, randomGPU);
		}
		else {
			if(ejecutarFuncion == 2) {
				mutacionPorIntercambio(poblacion, probabilidad, randomGPU);
			}
			else {
				if(ejecutarFuncion == 3) {
					mutacionPorInsercionSimple(poblacion, probabilidad, randomGPU);
				}
				else {
					if(ejecutarFuncion == 4) {
						mutacionPorInversionIVM(poblacion, probabilidad, randomGPU);
					}
					else {
						if(ejecutarFuncion == 5) {
							mutacionPorMezcla(poblacion, probabilidad, randomGPU);
						}
					}
				}
			}
		}
	}

}


__device__ void mutacionReal(float* poblacion, int probabilidad, struct Random randomGPU, int ejecutarFuncion, int generacionActual){

	if(ejecutarFuncion == 0) {
		mutacionUniforme(poblacion, probabilidad, randomGPU);
	}
	else{
		if(ejecutarFuncion == 1){
			mutacionNoUniforme(poblacion, probabilidad, randomGPU, generacionActual);
		}
	}
}

/******************* Implementacion de Funciones de Mutacion BINARIA *****************/

//Mutacion por inversion del valor del alelo
//Se modifica aleatoriamente (con probabilidad ''probabilidad') uno de los valores binarios del cromosoma
__device__ void mutacionPorInversion(int* poblacion, int probabilidad, struct Random randomGPU){
	cuPrintf("*** Mutacion por inversion ***\n");
	unsigned int r;
	int posicionNuevoCromosoma1 = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosomaDevice;
	if((getRandomInt(randomGPU, posicionNuevoCromosoma1) % 101) >= probabilidad ){
		r = (getRandomInt(randomGPU, posicionNuevoCromosoma1) % cantGenesEnCromosomaDevice);
		cuPrintf("r  %d\n", r);
		if(poblacion[posicionNuevoCromosoma1+r] == 0)
			poblacion[posicionNuevoCromosoma1+r] = 1;
		else
			poblacion[posicionNuevoCromosoma1+r] = 0;
	}
	if((getRandomInt(randomGPU, posicionNuevoCromosoma2) % 101) >= probabilidad ){
		r = (getRandomInt(randomGPU, posicionNuevoCromosoma2) % cantGenesEnCromosomaDevice);
		cuPrintf("r  %d\n", r);
		if(poblacion[posicionNuevoCromosoma2+r] == 0)
			poblacion[posicionNuevoCromosoma2+r] = 1;
		else
			poblacion[posicionNuevoCromosoma2+r] = 0;
	}
}

/******************* Implementacion de Funciones de Mutacion ENTERA *****************/

//Reajuste Aleatorio
//Cambia el valor de cada gen por otro valor posible, el nuevo valor se obtiene sorteando aleatoriamente
//entre todos los valores posibles.
__device__ void mutacionReajusteAleatorio(int* poblacion, int probabilidad, struct Random randomGPU){
	cuPrintf("*** Mutacion por reajuste aleatorio ***\n");
	unsigned int prob;
	unsigned int valor;
	int posicionNuevoCromosoma1 = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosomaDevice;
	for(int i=0; i<cantGenesEnCromosomaDevice; i++){
		prob = (getRandomInt(randomGPU, posicionNuevoCromosoma1 + i) % 101);

		if(prob <= probabilidad ){
			valor  = (getRandomInt(randomGPU, posicionNuevoCromosoma1)% 10);
			poblacion[posicionNuevoCromosoma1+i] = valor;
		}

		prob = (getRandomInt(randomGPU, posicionNuevoCromosoma1 + i) % 101);
		if(prob <= probabilidad ){
			valor = (getRandomInt(randomGPU, posicionNuevoCromosoma2)% 10);
			poblacion[posicionNuevoCromosoma2+i] = valor;
		}
	}
}

//Mutación por Deslizamiento
//Suma un valor que puede ser tanto positivo como negativo al valor de cada gen. Los valores a sumar son
//elegidos aleatoriamente para cada posición.
__device__ void mutacionPorDeslizamiento(int* poblacion, int probabilidad, struct Random randomGPU){
	cuPrintf("*** Mutacion por deslizamiento ***\n");
	unsigned int prob;
	unsigned int valor;
	int posicionNuevoCromosoma1 = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosomaDevice;
	//cuPrintf("cantGenesEnCromosomaDevice  %d\n", cantGenesEnCromosomaDevice);
	//cuPrintf("posicionNuevoCromosoma1  %d\n", posicionNuevoCromosoma1);
	//cuPrintf("posicionNuevoCromosoma2  %d\n", posicionNuevoCromosoma2);
	for(int i=0; i<cantGenesEnCromosomaDevice; i++){
		prob = (getRandomInt(randomGPU, posicionNuevoCromosoma1 + i) % 101);

		if(prob <= probabilidad ){
			valor  = (getRandomInt(randomGPU, posicionNuevoCromosoma1)% 10);
			poblacion[posicionNuevoCromosoma1+i] +=valor;
		}

		prob = (getRandomInt(randomGPU, posicionNuevoCromosoma1 + i) % 101);
		if(prob <= probabilidad ){
			valor  = (getRandomInt(randomGPU, posicionNuevoCromosoma2)% 10);
			poblacion[posicionNuevoCromosoma2+i] +=valor;
		}
	}
}

/******************* Implementacion de Funciones de Mutacion PERMUTACION *****************/

__device__ void mutacionPorIntercambio(int* poblacion, int probabilidad, struct Random randomGPU){
	cuPrintf("*** Mutacion por intercambio ***\n");
	//unsigned int valor;
	int posicionNuevoCromosoma1 = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosomaDevice;

	unsigned int prob = (getRandomInt(randomGPU, posicionNuevoCromosoma1) % 101);
	if(prob <= probabilidad ){
		int seleccion1 = (getRandomInt(randomGPU, posicionNuevoCromosoma1) % cantGenesEnCromosomaDevice);
		int seleccion2 = (getRandomInt(randomGPU, posicionNuevoCromosoma1 + 1) % cantGenesEnCromosomaDevice);
		cuPrintf("seleccion1  %d\n", seleccion1);
		cuPrintf("seleccion2  %d\n", seleccion2);
		int aux = poblacion[posicionNuevoCromosoma1 + seleccion1];
		poblacion[posicionNuevoCromosoma1 + seleccion1] = poblacion[posicionNuevoCromosoma1 + seleccion2];
		poblacion[posicionNuevoCromosoma1 + seleccion2] = aux;

	}
	prob = (getRandomInt(randomGPU, posicionNuevoCromosoma2) % 101);
	if(prob <= probabilidad ){
		int seleccion1 = (getRandomInt(randomGPU, posicionNuevoCromosoma2) % cantGenesEnCromosomaDevice);
		int seleccion2 = (getRandomInt(randomGPU, posicionNuevoCromosoma2 + 1) % cantGenesEnCromosomaDevice);
		cuPrintf("seleccion1  %d\n", seleccion1);
		cuPrintf("seleccion2  %d\n", seleccion2);
		int aux = poblacion[posicionNuevoCromosoma2 + seleccion1];
		poblacion[posicionNuevoCromosoma2 + seleccion1] = poblacion[posicionNuevoCromosoma2 + seleccion2];
		poblacion[posicionNuevoCromosoma2 + seleccion2] = aux;
	}
}

__device__ void mutacionPorInsercionSimple(int* poblacion, int probabilidad, struct Random randomGPU){
	cuPrintf("*** Mutacion por insercion simple ***\n");
	//unsigned int valor;
	int posicionNuevoCromosoma1 = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosomaDevice;
	int i = 0;
	unsigned int r3;
	int aux;
	unsigned int r1, r2;

	unsigned int prob = (getRandomInt(randomGPU, posicionNuevoCromosoma1) % 101);
	if(prob < probabilidad ){
		r1 = getRandomInt(randomGPU, posicionNuevoCromosoma1) % cantGenesEnCromosomaDevice;
		r2 = getRandomInt(randomGPU, posicionNuevoCromosoma2) % cantGenesEnCromosomaDevice;

		while(r2 == r1){
			r2 = getRandomInt(randomGPU, posicionNuevoCromosoma2+i) % cantGenesEnCromosomaDevice;
			i++;
		}

		if(r2 < r1){
			r3 = r1;
			r1 = r2;
			r2 = r3;
		}
		cuPrintf("r1  %d\n", r1);
		cuPrintf("r2  %d\n", r2);

		aux = poblacion[posicionNuevoCromosoma1+r2];
	    for (int i=r2; i>r1+1; i--){
	    	poblacion[posicionNuevoCromosoma1+i] = poblacion[posicionNuevoCromosoma1+i-1];
	    }
	    poblacion[posicionNuevoCromosoma1+r1+1] = aux;

	}

	prob = (getRandomInt(randomGPU, posicionNuevoCromosoma2) % 101);
	if(prob < probabilidad ){
		r1 = getRandomInt(randomGPU, posicionNuevoCromosoma1 + 1) % cantGenesEnCromosomaDevice;
		r2 = getRandomInt(randomGPU, posicionNuevoCromosoma2 + 1) % cantGenesEnCromosomaDevice;

		i = 0;
		while(r2 == r1){
			r2 = getRandomInt(randomGPU, posicionNuevoCromosoma2+i) % cantGenesEnCromosomaDevice;
			i++;
		}

		if(r2 < r1){
			r3 = r1;
			r1 = r2;
			r2 = r3;
		}
		cuPrintf("r1  %d\n", r1);
		cuPrintf("r2  %d\n", r2);

		aux = poblacion[posicionNuevoCromosoma2+r2];
		for (int i=r2; i>r1+1; i--){
			poblacion[posicionNuevoCromosoma2+i] = poblacion[posicionNuevoCromosoma2+i-1];
		}
		poblacion[posicionNuevoCromosoma2+r1+1] = aux;

	}
}


__device__ void mutacionPorInversionIVM(int* poblacion, int probabilidad, struct Random randomGPU){
	cuPrintf("*** Mutacion por inversion IVM ***\n");
//	unsigned int valor;
	int posicionNuevoCromosoma1 = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosomaDevice;
	int i = 0;
	unsigned int r3;
	//int aux;
	unsigned int r1, r2;
	//int tamArreglo;
	int tam, tam2, j;

	unsigned int prob = (getRandomInt(randomGPU, posicionNuevoCromosoma1) % 101);
	if(prob < probabilidad ){
		r1 = getRandomInt(randomGPU, posicionNuevoCromosoma1) % cantGenesEnCromosomaDevice;
		r2 = getRandomInt(randomGPU, posicionNuevoCromosoma2) % cantGenesEnCromosomaDevice;

		while(r2 == r1){
			r2 = getRandomInt(randomGPU, posicionNuevoCromosoma2+i) % cantGenesEnCromosomaDevice;
			i++;
		}

		if(r2 < r1){
			r3 = r1;
			r1 = r2;
			r2 = r3;
		}

		cuPrintf("r1  %d\n", r1);
		cuPrintf("r2  %d\n", r2);
		tam = r2-r1+1;
		tam2 = r2;
		//tamArreglo = (tam)*sizeof(int);
		int arregloAux[20];

		for (int i=0; i<tam; i++){
			arregloAux[i] = poblacion[posicionNuevoCromosoma1+tam2];
			tam2--;
			cuPrintf("%d", arregloAux[i]);
			cuPrintf("-");
		}

		j = 0;
		for (int i=r1; i<=r2; i++){
			poblacion[posicionNuevoCromosoma1+i] = arregloAux[j];
			j++;
		}
	}

	prob = (getRandomInt(randomGPU, posicionNuevoCromosoma2) % 101);
	if(prob < probabilidad ){
		r1 = getRandomInt(randomGPU, posicionNuevoCromosoma1 + 1) % cantGenesEnCromosomaDevice;
		r2 = getRandomInt(randomGPU, posicionNuevoCromosoma2 + 1) % cantGenesEnCromosomaDevice;

		i = 0;
		while(r2 == r1){
			r2 = getRandomInt(randomGPU, posicionNuevoCromosoma2+i) % cantGenesEnCromosomaDevice;
			i++;
		}

		if(r2 < r1){
			r3 = r1;
			r1 = r2;
			r2 = r3;
		}

		cuPrintf("r1  %d\n", r1);
		cuPrintf("r2  %d\n", r2);

		tam = r2-r1+1;
		tam2 = r2;
		//tamArreglo = (tam)*sizeof(int);
		int arregloAux2[20];

		for (int i=0; i<tam; i++){
			arregloAux2[i] = poblacion[posicionNuevoCromosoma2+tam2];
			tam2--;
			cuPrintf("%d", arregloAux2[i]);
			cuPrintf("-");
		}
		j = 0;
		for (int i=r1; i<=r2; i++){
			poblacion[posicionNuevoCromosoma2+i] = arregloAux2[j];
			j++;
		}
	}
}


__device__ void mutacionPorMezcla(int* poblacion, int probabilidad, struct Random randomGPU){
	cuPrintf("*** Mutacion por mezcla ***\n");
	int posicionNuevoCromosoma1 = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosomaDevice;

	//Esto va a ser un numero aleatorio hay que cambiarlo cuando sepamos como hacer arreglos dinamicos en CUDA!!!!
	int cantMezclados = 4;
	int arregloPosiciones[4];
	int arregloValores[4];
	int arregloPosicionesMezcladas[4];
	unsigned int r;
	unsigned int prob = (getRandomInt(randomGPU, posicionNuevoCromosoma1) % 101);
	if (prob < probabilidad ){
		for (int i = 0; i<cantMezclados; i++){
			r = (getRandomInt(randomGPU, posicionNuevoCromosoma1)% cantGenesEnCromosomaDevice);
			while(estaPresente(r, arregloPosiciones, 0, i)){
				r = (r+1) % cantGenesEnCromosomaDevice;
			}
			arregloPosiciones[i] = r;
		}

		for (int i = 0; i<cantMezclados; i++){
			r = (getRandomInt(randomGPU, posicionNuevoCromosoma1)% cantMezclados);
			while(estaPresente(arregloPosiciones[r], arregloPosicionesMezcladas, 0, i)){
				r = (r+1) % cantMezclados;
			}
			arregloPosicionesMezcladas[i] = arregloPosiciones[r];
			arregloValores[i] = poblacion[posicionNuevoCromosoma1+arregloPosiciones[r]];
		}

		for (int i = 0; i<cantMezclados; i++){
			poblacion[posicionNuevoCromosoma1 + arregloPosiciones[i]] = arregloValores[i];
		}
	}

	prob = (getRandomInt(randomGPU, posicionNuevoCromosoma2) % 101);
	if (prob < probabilidad ){
		for (int i = 0; i<cantMezclados; i++){
			r = (getRandomInt(randomGPU, posicionNuevoCromosoma2)% cantGenesEnCromosomaDevice);
			while(estaPresente(r, arregloPosiciones, 0, i)){
				r = (r+1) % cantGenesEnCromosomaDevice;
			}
			arregloPosiciones[i] = r;
		}

		for (int i = 0; i<cantMezclados; i++){
			r = (getRandomInt(randomGPU, posicionNuevoCromosoma2)% cantMezclados);
			while(estaPresente(arregloPosiciones[r], arregloPosicionesMezcladas, 0, i)){
				r = (r+1) % cantMezclados;
			}
			arregloPosicionesMezcladas[i] = arregloPosiciones[r];
			arregloValores[i] = poblacion[posicionNuevoCromosoma2+arregloPosiciones[r]];
		}

		for (int i = 0; i<cantMezclados; i++){
			poblacion[posicionNuevoCromosoma2 + arregloPosiciones[i]] = arregloValores[i];
		}
	}


	cuPrintf("\nPosiciones\n");
	for (int i =0; i<4;i++) {
	  cuPrintf("%d ",arregloPosiciones[i]);
	}

	cuPrintf("\nPosiciones Mezcladas\n");
	for (int i =0; i<4;i++) {
	  cuPrintf("%d ",arregloPosicionesMezcladas[i]);
	}

	cuPrintf("\nValores\n");
	for (int i =0; i<4;i++) {
	  cuPrintf("%d ",arregloValores[i]);
	}

}

/******************* Implementacion de Funciones de Mutacion REAL *****************/

__device__ void mutacionUniforme(float* poblacion, int probabilidad, struct Random randomGPU){
	cuPrintf("*** Mutacion uniforme real ***\n");
	int posicionNuevoCromosoma1 = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosomaDevice;

	unsigned int k;
	float left_k, right_k, aux1;
	float aux;
	unsigned int prob = (getRandomInt(randomGPU, posicionNuevoCromosoma1) % 101);
	if (prob < probabilidad ){
		k = getRandomInt(randomGPU, posicionNuevoCromosoma1 + 1) % cantGenesEnCromosomaDevice;
		//cuPrintf("k = %d \n",k);
		left_k = poblacion[posicionNuevoCromosoma1 + (k - 1) % cantGenesEnCromosomaDevice];
		right_k = poblacion[posicionNuevoCromosoma1 + (k + 1) % cantGenesEnCromosomaDevice];
		aux = getRandom(randomGPU, posicionNuevoCromosoma1);
		//cuPrintf("aux = %f \n",aux);
		if (left_k > right_k){
			aux1 = right_k;
			right_k = left_k;
			left_k = aux1;
		}
		poblacion[posicionNuevoCromosoma1 + k] = left_k + aux * (right_k - left_k);
	}

	prob = (getRandomInt(randomGPU, posicionNuevoCromosoma2) % 101);
	if (prob < probabilidad ){
		k = getRandomInt(randomGPU, posicionNuevoCromosoma2 + 1) % cantGenesEnCromosomaDevice;
		//cuPrintf("- individuo 2  -\n k = %d \n",k);
		left_k = poblacion[posicionNuevoCromosoma2 + (k - 1) % cantGenesEnCromosomaDevice];
		right_k = poblacion[posicionNuevoCromosoma2 + (k + 1) % cantGenesEnCromosomaDevice];
		aux = getRandom(randomGPU, posicionNuevoCromosoma2);
		//cuPrintf("aux = %f \n",aux);
		if (left_k > right_k){
			aux1 = right_k;
			right_k = left_k;
			left_k = aux1;
		}
		poblacion[posicionNuevoCromosoma2 + k] = left_k + aux * (right_k - left_k);
	}
}

__device__ void mutacionNoUniforme(float* poblacion, int probabilidad, struct Random randomGPU, int generacionActual){
	cuPrintf("*** Mutacion no uniforme real ***\n");
	int posicionNuevoCromosoma1 = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosomaDevice;

	unsigned int k;
	float left_k, right_k, resultado, r;
	int aux;
	unsigned int prob = (getRandomInt(randomGPU, posicionNuevoCromosoma1) % 101);
	if (prob < probabilidad ){
		k = getRandomInt(randomGPU, posicionNuevoCromosoma1 + 1) % cantGenesEnCromosomaDevice;
		cuPrintf("k = %d \n",k);
		left_k = poblacion[posicionNuevoCromosoma1 + ((k - 1) % cantGenesEnCromosomaDevice)];
		right_k = poblacion[posicionNuevoCromosoma1 + ((k + 1) % cantGenesEnCromosomaDevice)];
		aux = getRandomInt(randomGPU, posicionNuevoCromosoma1) % 1;
		cuPrintf("aux = %f \n",aux);
		r = getRandom(randomGPU, posicionNuevoCromosoma1);
		if (aux == 0){
		/*	cuPrintf("generacionActual = %d \n", generacionActual);
			cuPrintf("numeroGeneracionesDevice = %d\n", numeroGeneracionesDevice);
			cuPrintf("r = %f\n", r);
			cuPrintf("pow = %f\n", __powf((1 - (generacionActual/numeroGeneracionesDevice)), gradoNoUniformidad));
		*/
			resultado = poblacion[posicionNuevoCromosoma1 + k] + ((right_k - poblacion[posicionNuevoCromosoma1 + k]) * r * (__powf((1 - (generacionActual/numeroGeneracionesDevice)), gradoNoUniformidad)));
		}
		else {
		/*	cuPrintf("generacionActual = %d \n", generacionActual);
			cuPrintf("numeroGeneracionesDevice = %d\n", numeroGeneracionesDevice);
			cuPrintf("r = %f\n", r);
			cuPrintf("pow = %f\n", __powf((1 - (generacionActual/numeroGeneracionesDevice)), gradoNoUniformidad));
		*/
			resultado = poblacion[posicionNuevoCromosoma1 + k] - ((poblacion[posicionNuevoCromosoma1 + k] - left_k) * r * (__powf((1 - (generacionActual/numeroGeneracionesDevice)), gradoNoUniformidad)));
		}
		poblacion[posicionNuevoCromosoma1 + k] = resultado;
	}

	prob = (getRandomInt(randomGPU, posicionNuevoCromosoma2) % 101);
	if (prob < probabilidad ){
		k = getRandomInt(randomGPU, posicionNuevoCromosoma2 + 1) % cantGenesEnCromosomaDevice;
		cuPrintf("k = %d \n",k);
		left_k = poblacion[posicionNuevoCromosoma2 + ((k - 1) % cantGenesEnCromosomaDevice)];
		right_k = poblacion[posicionNuevoCromosoma2 + ((k + 1) % cantGenesEnCromosomaDevice)];
		aux = getRandomInt(randomGPU, posicionNuevoCromosoma2) % 1;
		cuPrintf("aux = %f \n",aux);
		r = getRandom(randomGPU, posicionNuevoCromosoma2);
		if (aux == 0){
			resultado = poblacion[posicionNuevoCromosoma2 + k] + ((right_k - poblacion[posicionNuevoCromosoma2 + k]) * r * (__powf((1 - (generacionActual/numeroGeneracionesDevice)), gradoNoUniformidad)));
		}
		else {
			resultado = poblacion[posicionNuevoCromosoma2 + k] - ((poblacion[posicionNuevoCromosoma2 + k] - left_k) * r * (__powf((1 - (generacionActual/numeroGeneracionesDevice)), gradoNoUniformidad)));
		}
		poblacion[posicionNuevoCromosoma2 + k] = resultado;
	}
}


/******************* Implementacion de Funciones de fitness *****************/

__device__ int calcularFitnessBinaria(int* poblacion, int posicion) {
	int fitness = 0;
	float contador = cantGenesEnCromosomaDevice-1;
	for(int i=0; i<cantGenesEnCromosomaDevice; i++){
	  fitness = fitness + poblacion[posicion + i] * __powf(2.0,contador);
	  contador--;
	}
	return fitness;
}

__device__ int calcularFitnessEntera(int* poblacion, int posicion) {
	int fitness = 0;
	float contador = cantGenesEnCromosomaDevice-1;
	for(int i=0; i<cantGenesEnCromosomaDevice; i++){
	  fitness = fitness + poblacion[posicion + i];
	  contador--;
	}
	return fitness;
}

__device__ float calcularFitnessReal(float* poblacion, int posicion) {
	float fitness = 0;
	float contador = cantGenesEnCromosomaDevice-1;
	for(int i=0; i<cantGenesEnCromosomaDevice; i++){
	  fitness = fitness + poblacion[posicion + i];
	  contador--;
	}
	return fitness;
}


/* Obtengo los parametros de la clase parser.cpp y los asigno a las variables globales
 *
 * [0] = representacion
 * [1] = cantidadDePoblacionesEnGpu
 * [2] = cantidadDePoblacionesEnCpu
 * [3] = tamanioPoblacion
 * [4] = cantidadGenesPorIndividuo
 * [5] = metodoDeSeleccion
 * [6] = operadorCruzamiento
 * [7] = operadorMutacion
 * [8] = probabilidadCruzamiento
 * [9] = probabilidadMutacion
 * [10] = mecanismosDeReemplazo
 * [11] = numeroGeneraciones
 * [12] = cantidadDeHilosEnLasPoblacionesDeLaGpu
 * [13] = cantidadDeHilosEnLasPoblacionesDeLaCpu
 * [14] = memoriaCompartidaEnLaGpu
 * [15] = calcularFitnessEnGpu
 * [16] = criterioDeParada
 * [17] = funcionFitness
 * [18] = gradoNoUniformidad
 *
 */
__global__ void asignarParametrosDevice(int * parametros){
	cantidadPoblacionesDevice = parametros[1];
	cantCromosomasPoblacionDevice = parametros[3];
	cantGenesEnCromosomaDevice = parametros[4];
	totalGenesEnPoblacionDevice = cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice;
	tipoSeleccion = parametros[5];
	tipoCruzamiento = parametros[6];
	tipoMutacion = parametros[7];
	probabilidadCruzamiento = parametros[8];
	probabilidadMutacion = parametros[9];
	tipoReemplazo = parametros[10];
	numeroGeneracionesDevice = parametros[11];
	gradoNoUniformidad = parametros[18];
}

/******************* Implementacion de Funciones del host *****************/

//__host__ puede ponerse o no
/*
 * Idem a asignarParametrosDevice()
 */
void asignarParametros(int * parametros){
	tipoRepresentacion = parametros[0];
	cantidadPoblaciones = parametros[1];
	cantCromosomasPoblacion = parametros[3];
	cantGenesEnCromosoma = parametros[4];
	numeroGeneraciones = parametros[11];
}

void representacionBinaria() {
	int tamVectorPoblacion = sizeof(int)*totalGenes;
	int tamVectorFitness = sizeof(int)*totalIndividuos;
	int tamVectorIndividuosAMigrar = sizeof(int)*cantGenesEnCromosoma*cantidadPoblaciones;

	static int *poblacionActualHost = (int *) malloc(tamVectorPoblacion);
	static int *nuevaPoblacionHost = (int *) malloc(tamVectorPoblacion);
	static int *fitnessActualHost = (int *) malloc(tamVectorFitness);
	static int *nuevoFitnessHost = (int *) malloc(tamVectorFitness);
	static int *individuosAMigrarHost = (int *) malloc(tamVectorIndividuosAMigrar);

	int *poblacionActualDevice;
	int *nuevaPoblacionDevice;
	int *fitnessActualDevice;
	int *nuevoFitnessDevice;
	int *individuosAMigrarDevice;

	// alocar en el device
	cudaMalloc(&poblacionActualDevice, tamVectorPoblacion);
	cudaMalloc(&nuevaPoblacionDevice, tamVectorPoblacion);
	cudaMalloc(&fitnessActualDevice, tamVectorFitness);
	cudaMalloc(&nuevoFitnessDevice, tamVectorFitness);
	cudaMalloc(&individuosAMigrarDevice, tamVectorIndividuosAMigrar);

	//Semilla randomica.
    //	srand48(SEMILLA);

	//Inicio numeros aleatorios.
	struct Random r = initRandom(cantCromosomasPoblacion/2);

	//cuantos bloques? (1)
	dim3 tamGrid(1, cantidadPoblaciones); //Grid dimensión
	//cuantos hilos por bloque? (N) o sea tengo un bloque y n hilos (un hilo por columna)
	dim3 tamBlock(1,cantCromosomasPoblacion/2,1); //Block dimensión

	dim3 tamBlockMigracion(1,1,1); //Block dimensión

	//inicializacion de la poblacion en GPU
	inicializarPoblacion<<<tamGrid, tamBlock>>>(poblacionActualDevice, fitnessActualDevice, r, cantCromosomasPoblacion, tipoRepresentacion);

	// Traer resultado
	cudaMemcpy (fitnessActualHost, fitnessActualDevice, tamVectorFitness, cudaMemcpyDeviceToHost);
	cudaMemcpy (poblacionActualHost, poblacionActualDevice, tamVectorPoblacion, cudaMemcpyDeviceToHost);

	cout << "\nPOBLACION INICIAL\n";
	for (int i = 0; i<totalGenes; i++){
		if(i%cantGenesEnCromosoma == 0){
			cout << "\n";

		}
		printf("%i ", poblacionActualHost[i]);
	}
	cout << "\n\n\nFITNESS\n";

	for (int i = 0; i<cantCromosomasPoblacion * cantidadPoblaciones; i++){
		printf("%i ", fitnessActualHost[i]);
	}
	cout << "\n\n\n\n";

	//int generacionesMigrar = 1;
	int iteracion = 0;
	//los individuos de la poblacion actual se van intercambiando en el reemplazo
	while(iteracion < numeroGeneraciones){

		//si la iteracion es par invoco a GPU con poblacionActual, nuevaPoblacion
		if ((iteracion % 2) == 0) {
			// seleccionamos, cruzamos y mutamos en GPU
			mainKernelBinaria<<<tamGrid, tamBlock>>>(poblacionActualDevice, nuevaPoblacionDevice, fitnessActualDevice, nuevoFitnessDevice, individuosAMigrarDevice, r);

			//Si hay que migrar
			//if (iteracion % generacionesMigrar == 0) {
			//	migracionRepresentacionBinaria<<<tamGrid, tamBlockMigracion>>>(nuevaPoblacionDevice, nuevoFitnessDevice, individuosAMigrarDevice, r);
			//}
		}
		//sino invoco a GPU con nuevaPoblacion, poblacionActual
		else {

			// seleccionamos, cruzamos y mutamos en GPU
			mainKernelBinaria<<<tamGrid, tamBlock>>>(nuevaPoblacionDevice, poblacionActualDevice, nuevoFitnessDevice, fitnessActualDevice, individuosAMigrarDevice, r);

			//Si hay que migrar
			//if (iteracion % generacionesMigrar == 0) {
			//	migracionRepresentacionBinaria<<<tamGrid, tamBlockMigracion>>>(poblacionActualDevice, fitnessActualDevice, individuosAMigrarDevice, r);
			//}
		}

		//mejorFitness = obtenerMejorFitness(poblacionActual,mejorCromosoma);
		iteracion++;
	}

	// Traer resultado
	cudaMemcpy (nuevoFitnessHost, nuevoFitnessDevice, tamVectorFitness, cudaMemcpyDeviceToHost);
	cudaMemcpy (nuevaPoblacionHost, nuevaPoblacionDevice, tamVectorPoblacion, cudaMemcpyDeviceToHost);
	cudaMemcpy (individuosAMigrarHost, individuosAMigrarDevice, tamVectorIndividuosAMigrar, cudaMemcpyDeviceToHost);

	// liberar memoria de las matrices en el device
	cudaFree(fitnessActualDevice);
	cudaFree(poblacionActualDevice);
	cudaFree(nuevoFitnessHost);
	cudaFree(nuevaPoblacionDevice);

	cout << "NUEVA POBLACION\n";
	for (int i = 0; i<totalGenes; i++){
		if(i%cantGenesEnCromosoma == 0){
			cout << "\n";

		}
		printf("%i ", nuevaPoblacionHost[i]);
	}
	cout << "\n\n\nFITNESS\n";

	for (int i = 0; i<cantCromosomasPoblacion * cantidadPoblaciones; i++){
		printf("%i ", nuevoFitnessHost[i]);
	}
	cout << "\n";

/*	cout << "\n\nA MIGRAR\n";

	for (int i = 0; i<cantGenesEnCromosoma * cantidadPoblaciones; i++){
		if(i%cantGenesEnCromosoma == 0){
			cout << "\n";
		}
		printf("%i ", individuosAMigrarHost[i]);
	}
	cout << "\n";
*/
}

void representacionEntera() {
	int tamVectorPoblacion = sizeof(int)*totalGenes;
	int tamVectorFitness = sizeof(int)*totalIndividuos;
	int tamVectorIndividuosAMigrar = sizeof(int)*cantGenesEnCromosoma*cantidadPoblaciones;

	static int *poblacionActualHost = (int *) malloc(tamVectorPoblacion);
	static int *nuevaPoblacionHost = (int *) malloc(tamVectorPoblacion);
	static int *fitnessActualHost = (int *) malloc(tamVectorFitness);
	static int *nuevoFitnessHost = (int *) malloc(tamVectorFitness);
	static int *individuosAMigrarHost = (int *) malloc(tamVectorIndividuosAMigrar);

	int *poblacionActualDevice;
	int *nuevaPoblacionDevice;
	int *fitnessActualDevice;
	int *nuevoFitnessDevice;
	int *individuosAMigrarDevice;

	// alocar en el device
	cudaMalloc(&poblacionActualDevice, tamVectorPoblacion);
	cudaMalloc(&nuevaPoblacionDevice, tamVectorPoblacion);
	cudaMalloc(&fitnessActualDevice, tamVectorFitness);
	cudaMalloc(&nuevoFitnessDevice, tamVectorFitness);
	cudaMalloc(&individuosAMigrarDevice, tamVectorIndividuosAMigrar);

	//Semilla randomica.
	//	srand48(SEMILLA);

	//Inicio numeros aleatorios.
	struct Random r = initRandom(cantCromosomasPoblacion/2);

	//cuantos bloques? (1)
	dim3 tamGrid(1, cantidadPoblaciones); //Grid dimensión
	//cuantos hilos por bloque? (N) o sea tengo un bloque y n hilos (un hilo por columna)
	dim3 tamBlock(1,cantCromosomasPoblacion/2,1); //Block dimensión

	dim3 tamBlockMigracion(1,1,1); //Block dimensión

	//inicializacion de la poblacion en GPU
	inicializarPoblacion<<<tamGrid, tamBlock>>>(poblacionActualDevice, fitnessActualDevice, r, cantCromosomasPoblacion, tipoRepresentacion);

	// Traer resultado
	cudaMemcpy (fitnessActualHost, fitnessActualDevice, tamVectorFitness, cudaMemcpyDeviceToHost);
	cudaMemcpy (poblacionActualHost, poblacionActualDevice, tamVectorPoblacion, cudaMemcpyDeviceToHost);

	cout << "\nPOBLACION INICIAL\n";
	for (int i = 0; i<totalGenes; i++){
		if(i%cantGenesEnCromosoma == 0){
			cout << "\n";

		}
		printf("%i ", poblacionActualHost[i]);
	}
	cout << "\n\n\nFITNESS\n";

	for (int i = 0; i<cantCromosomasPoblacion * cantidadPoblaciones; i++){
		printf("%i ", fitnessActualHost[i]);
	}
	cout << "\n\n\n\n";

	//int generacionesMigrar = 1;
	int iteracion = 0;
	//los individuos de la poblacion actual se van intercambiando en el reemplazo
	while(iteracion < numeroGeneraciones){

		//si la iteracion es par invoco a GPU con poblacionActual, nuevaPoblacion
		if ((iteracion % 2) == 0) {
			// seleccionamos, cruzamos y mutamos en GPU
			mainKernelEntera<<<tamGrid, tamBlock>>>(poblacionActualDevice, nuevaPoblacionDevice, fitnessActualDevice, nuevoFitnessDevice, individuosAMigrarDevice, r);

			//Si hay que migrar
			//if (iteracion % generacionesMigrar == 0) {
			//	migracionRepresentacionBinaria<<<tamGrid, tamBlockMigracion>>>(nuevaPoblacionDevice, nuevoFitnessDevice, individuosAMigrarDevice, r);
			//}
		}
		//sino invoco a GPU con nuevaPoblacion, poblacionActual
		else {

			// seleccionamos, cruzamos y mutamos en GPU
			mainKernelEntera<<<tamGrid, tamBlock>>>(nuevaPoblacionDevice, poblacionActualDevice, nuevoFitnessDevice, fitnessActualDevice, individuosAMigrarDevice, r);

			//Si hay que migrar
			//if (iteracion % generacionesMigrar == 0) {
			//	migracionRepresentacionBinaria<<<tamGrid, tamBlockMigracion>>>(poblacionActualDevice, fitnessActualDevice, individuosAMigrarDevice, r);
			//}
		}

		//mejorFitness = obtenerMejorFitness(poblacionActual,mejorCromosoma);
		iteracion++;
	}

	// Traer resultado
	cudaMemcpy (nuevoFitnessHost, nuevoFitnessDevice, tamVectorFitness, cudaMemcpyDeviceToHost);
	cudaMemcpy (nuevaPoblacionHost, nuevaPoblacionDevice, tamVectorPoblacion, cudaMemcpyDeviceToHost);
	cudaMemcpy (individuosAMigrarHost, individuosAMigrarDevice, tamVectorIndividuosAMigrar, cudaMemcpyDeviceToHost);

	// liberar memoria de las matrices en el device
	cudaFree(fitnessActualDevice);
	cudaFree(poblacionActualDevice);
	cudaFree(nuevoFitnessHost);
	cudaFree(nuevaPoblacionDevice);

	cout << "NUEVA POBLACION\n";
	for (int i = 0; i<totalGenes; i++){
		if(i%cantGenesEnCromosoma == 0){
			cout << "\n";

		}
		printf("%i ", nuevaPoblacionHost[i]);
	}
	cout << "\n\n\nFITNESS\n";

	for (int i = 0; i<cantCromosomasPoblacion * cantidadPoblaciones; i++){
		printf("%i ", nuevoFitnessHost[i]);
	}
	cout << "\n";

/*	cout << "\n\nA MIGRAR\n";

	for (int i = 0; i<cantGenesEnCromosoma * cantidadPoblaciones; i++){
		if(i%cantGenesEnCromosoma == 0){
			cout << "\n";
		}
		printf("%i ", individuosAMigrarHost[i]);
	}
	cout << "\n";
*/
}


void representacionReal() {
	int tamVectorPoblacion = sizeof(float)*totalGenes;
	int tamVectorFitness = sizeof(float)*totalIndividuos;
	int tamVectorIndividuosAMigrar = sizeof(float)*cantGenesEnCromosoma*cantidadPoblaciones;

	static float *poblacionActualHost = (float *) malloc(tamVectorPoblacion);
	static float *nuevaPoblacionHost = (float *) malloc(tamVectorPoblacion);
	static float *fitnessActualHost = (float *) malloc(tamVectorFitness);
	static float *nuevoFitnessHost = (float *) malloc(tamVectorFitness);
	static float *individuosAMigrarHost = (float *) malloc(tamVectorIndividuosAMigrar);

	float *poblacionActualDevice;
	float *nuevaPoblacionDevice;
	float *fitnessActualDevice;
	float *nuevoFitnessDevice;
	float *individuosAMigrarDevice;

	// alocar en el device
	cudaMalloc(&poblacionActualDevice, tamVectorPoblacion);
	cudaMalloc(&nuevaPoblacionDevice, tamVectorPoblacion);
	cudaMalloc(&fitnessActualDevice, tamVectorFitness);
	cudaMalloc(&nuevoFitnessDevice, tamVectorFitness);
	cudaMalloc(&individuosAMigrarDevice, tamVectorIndividuosAMigrar);

	//Semilla randomica.
	//srand48(SEMILLA);

	//Inicio numeros aleatorios.
	struct Random r = initRandom(cantCromosomasPoblacion/2);

	//cuantos bloques? (1)
	dim3 tamGrid(1, cantidadPoblaciones); //Grid dimensión
	//cuantos hilos por bloque? (N) o sea tengo un bloque y n hilos (un hilo por columna)
	dim3 tamBlock(1,cantCromosomasPoblacion/2,1); //Block dimensión

	dim3 tamBlockMigracion(1,1,1); //Block dimensión

	//inicializacion de la poblacion en GPU
	inicializarPoblacionReal<<<tamGrid, tamBlock>>>(poblacionActualDevice, fitnessActualDevice, r, cantCromosomasPoblacion, 2);

	// Traer resultado
	cudaMemcpy (fitnessActualHost, fitnessActualDevice, tamVectorFitness, cudaMemcpyDeviceToHost);
	cudaMemcpy (poblacionActualHost, poblacionActualDevice, tamVectorPoblacion, cudaMemcpyDeviceToHost);

	cout << "tot " << totalGenes << " POBLACION INICIAL\n";
	cout << totalGenes;
	for (int i = 0; i<totalGenes; i++){
		if(i%cantGenesEnCromosoma == 0){
			cout << "\n";

		}
		printf("%f ", poblacionActualHost[i]);
	}
	cout << "\n\n\nFITNESS\n";

	for (int i = 0; i<cantCromosomasPoblacion * cantidadPoblaciones; i++){
		printf("%f ", fitnessActualHost[i]);
	}
	cout << "\n\n\n\n";

	//int generacionesMigrar = 1;
	int iteracion = 0;
	//los individuos de la poblacion actual se van intercambiando en el reemplazo
	while(iteracion < numeroGeneraciones){
		//si la iteracion es par invoco a GPU con poblacionActual, nuevaPoblacion
		if ((iteracion % 2) == 0) {
			// seleccionamos, cruzamos y mutamos en GPU
			mainKernelReal<<<tamGrid, tamBlock>>>(poblacionActualDevice, nuevaPoblacionDevice, fitnessActualDevice, nuevoFitnessDevice, individuosAMigrarDevice, r, iteracion);

			//Si hay que migrar
			//if (iteracion % generacionesMigrar == 0) {
			//	migracionRepresentacionBinaria<<<tamGrid, tamBlockMigracion>>>(nuevaPoblacionDevice, nuevoFitnessDevice, individuosAMigrarDevice, r, iteracion);
			//}
		}
		//sino invoco a GPU con nuevaPoblacion, poblacionActual
		else {

			// seleccionamos, cruzamos y mutamos en GPU
			mainKernelReal<<<tamGrid, tamBlock>>>(nuevaPoblacionDevice, poblacionActualDevice, nuevoFitnessDevice, fitnessActualDevice, individuosAMigrarDevice, r, iteracion);

			//Si hay que migrar
			//if (iteracion % generacionesMigrar == 0) {
			//	migracionRepresentacionBinaria<<<tamGrid, tamBlockMigracion>>>(poblacionActualDevice, fitnessActualDevice, individuosAMigrarDevice, r);
			//}
		}

		//mejorFitness = obtenerMejorFitness(poblacionActual,mejorCromosoma);
		iteracion++;
	}

	// Traer resultado
	cudaMemcpy (nuevoFitnessHost, nuevoFitnessDevice, tamVectorFitness, cudaMemcpyDeviceToHost);
	cudaMemcpy (nuevaPoblacionHost, nuevaPoblacionDevice, tamVectorPoblacion, cudaMemcpyDeviceToHost);
	cudaMemcpy (individuosAMigrarHost, individuosAMigrarDevice, tamVectorIndividuosAMigrar, cudaMemcpyDeviceToHost);

	// liberar memoria de las matrices en el device
	cudaFree(fitnessActualDevice);
	cudaFree(poblacionActualDevice);
	cudaFree(nuevoFitnessHost);
	cudaFree(nuevaPoblacionDevice);

	cout << "NUEVA POBLACION\n";
	for (int i = 0; i<totalGenes; i++){
		if(i%cantGenesEnCromosoma == 0){
			cout << "\n";

		}
		printf("%f ", nuevaPoblacionHost[i]);
	}
	cout << "\n\n\nFITNESS\n";

	for (int i = 0; i<cantCromosomasPoblacion * cantidadPoblaciones; i++){
		printf("%f ", nuevoFitnessHost[i]);
	}
	cout << "\n";

	/*cout << "\n\nA MIGRAR\n";

	for (int i = 0; i<cantGenesEnCromosoma * cantidadPoblaciones; i++){
		if(i%cantGenesEnCromosoma == 0){
			cout << "\n";
		}
		printf("%i ", individuosAMigrarHost[i]);
	}
	cout << "\n";
	*/
}


//////////////////////
// Program main
//////////////////////

int main( int argc, char** argv) {
	cudaPrintfInit();
	int * parametros = obtenerParametros();
	asignarParametros(parametros);

	dim3 tamGridAsig(1,1); //Grid dimensión
	dim3 tamBlockAsig(1,1,1); //Block dimensión

	int tamVectorParametros = sizeof(int)*18;
	int *parametrosDevice;
	cudaMalloc(&parametrosDevice, tamVectorParametros);

	cudaMemcpy (parametrosDevice, parametros, tamVectorParametros, cudaMemcpyHostToDevice);
	//asignacion de parametros en GPU
	asignarParametrosDevice<<<tamGridAsig, tamBlockAsig>>>(parametrosDevice);

	totalGenes = cantidadPoblaciones * cantCromosomasPoblacion * cantGenesEnCromosoma;
	totalIndividuos = cantCromosomasPoblacion*cantidadPoblaciones;

	if (tipoRepresentacion == 0) {
		representacionBinaria();
	}
	else {
		if ((tipoRepresentacion == 1) || (tipoRepresentacion == 2)) {
			representacionEntera();
		}
		else {
			representacionReal();
		}
	}
	cudaPrintfDisplay(stdout, true);
	cudaPrintfEnd();

}
